package game.coord;

import game.Abstract2DMap;
import game.Position;

import java.util.ArrayList;
import java.util.List;

public class MapGenerator
{
    public static Position getRight(int index, Position size)
    {
        Position position = Abstract2DMap.calculatePosition(index, size);
        // RIGHT:
        Position RIGHT = new Position(position.getX() + 1, position.getY());
        if (RIGHT.getX() >= size.getX())
        {
            RIGHT.setX(0);
        }
        return RIGHT;
    }
    public static Position getBottom(int index, Position size)
    {
        Position position = Abstract2DMap.calculatePosition(index, size);
        // BOTTOM:
        Position BOTTOM = new Position(position.getX(), position.getY() + 1);
        if (BOTTOM.getY() >= size.getY())
        {
            BOTTOM.setY(0);
        }
        return BOTTOM;
    }
    public static Position getLeft(int index, Position size)
    {
        Position position = Abstract2DMap.calculatePosition(index, size);
        // LEFT:
        Position LEFT = new Position(position.getX() - 1, position.getY());
        if (LEFT.getX() < 0)
        {
            LEFT.setX(size.getX() - 1);
        }
        return LEFT;
    }
    public static Position getTop(int index, Position size)
    {
        Position position = Abstract2DMap.calculatePosition(index, size);
        // TOP:
        Position TOP = new Position(position.getX(), position.getY() - 1);
        if (TOP.getY() < 0)
        {
            TOP.setY(size.getY() - 1);
        }
        return TOP;
    }
    public static Position getBottomLeft(int index, Position size)
    {
        Position position = Abstract2DMap.calculatePosition(index, size);
        // BOTTOMLEFT:
        Position BOTTOMLEFT = new Position(position.getX(), position.getY());
        BOTTOMLEFT.setX(getLeft(index, size).getX());
        BOTTOMLEFT.setY(getBottom(index, size).getY());
        return BOTTOMLEFT;
    }
    public static Position getBottomRight(int index, Position size)
    {
        Position position = Abstract2DMap.calculatePosition(index, size);
        // BOTTOMRIGHT:
        Position BOTTOMRIGHT = new Position(position.getX(), position.getY());
        BOTTOMRIGHT.setX(getRight(index, size).getX());
        BOTTOMRIGHT.setY(getBottom(index, size).getY());
        return BOTTOMRIGHT;
    }
    public static Position getTopLeft(int index, Position size)
    {
        Position position = Abstract2DMap.calculatePosition(index, size);
        // TOPLEFT:
        Position TOPLEFT = new Position(position.getX(), position.getY());
        TOPLEFT.setX(getLeft(index, size).getX());
        TOPLEFT.setY(getTop(index, size).getY());
        return TOPLEFT;
    }
    public static Position getTopRight(int index, Position size)
    {
        Position position = Abstract2DMap.calculatePosition(index, size);
        // TOPRIGHT:
        Position TOPRIGHT = new Position(position.getX(), position.getY());
        TOPRIGHT.setX(getRight(index, size).getX());
        TOPRIGHT.setY(getTop(index, size).getY());
        return TOPRIGHT;
    }
    
    private static <TYPE extends Object> List<TYPE> generateList(int count, Class<TYPE> clazz)
    {
        List<TYPE> list = new ArrayList<TYPE>(count);
        while (list.size() < count)
        {
            try
            {
                list.add(clazz.newInstance());
            }
            catch (InstantiationException | IllegalAccessException e)
            {
                e.printStackTrace();
            }
        }
        return list;
    }
    
    private static <TYPE extends Object> TYPE getCell(Position size, List<TYPE> cells, Position position)
    {
        int index = Abstract2DMap.calculateIndex(position.getX(), position.getY(), size);
        return cells.get(index);
    }
    
    public static <TYPE extends MooreCell<TYPE>> List<TYPE> generateMooreMap(Position size, Class<TYPE> cellClass)
    {
        int sumCells = size.getSum();
        List<TYPE> list = generateList(sumCells, cellClass);
        rebuildMooreMap(size, list);
        return list;
    }
    
    public static <TYPE extends MooreCell<TYPE>> void rebuildMooreMap(Position size, List<TYPE> cells)
    {
        if (cells.size() != size.getSum())
        {
            throw new IllegalArgumentException("cells.size(" + cells.size() + ") != size.getSum(" + size.getSum() + ")");
        }
        for (int index = 0; index < cells.size(); index++)
        {
            TYPE cell = cells.get(index);
            cell.setRight(getCell(size, cells, getRight(index, size)));
            cell.setBottom(getCell(size, cells, getBottom(index, size)));
            cell.setLeft(getCell(size, cells, getLeft(index, size)));
            cell.setTop(getCell(size, cells, getTop(index, size)));
            cell.setBottomLeft(getCell(size, cells, getBottomLeft(index, size)));
            cell.setBottomRight(getCell(size, cells, getBottomRight(index, size)));
            cell.setTopLeft(getCell(size, cells, getTopLeft(index, size)));
            cell.setTopRight(getCell(size, cells, getTopRight(index, size)));
        }
    }
    
    public static <TYPE extends VonNeumannCell<TYPE>> List<TYPE> generateVonNeumannMap(Position size, Class<TYPE> cellClass)
    {
        int sumCells = size.getSum();
        List<TYPE> list = generateList(sumCells, cellClass);
        rebuildVonNeumannMap(size, list);
        return list;
    }
    
    public static <TYPE extends VonNeumannCell<TYPE>> void rebuildVonNeumannMap(Position size, List<TYPE> cells)
    {
        if (cells.size() != size.getSum())
        {
            throw new IllegalArgumentException("cells.size(" + cells.size() + ") != size.getSum(" + size.getSum() + ")");
        }
        for (int index = 0; index < cells.size(); index++)
        {
            TYPE cell = cells.get(index);
            cell.setRight(getCell(size, cells, getRight(index, size)));
            cell.setBottom(getCell(size, cells, getBottom(index, size)));
            cell.setLeft(getCell(size, cells, getLeft(index, size)));
            cell.setTop(getCell(size, cells, getTop(index, size)));
        }
    }
}
